def a_to_r(anum: int) -> str:
    '''Convert arabic numeral to roman.'''
    if not isinstance(anum, int):
        raise TypeError("a_to_r() only takes a decimal integer")
    if not 0 < anum < 4000:
        raise ValueError("integer value requested to be between 1 and 3999 for RomanNum()")
    rnum = ''
    for (rchars, achars) in RomanNum._RomanNum__reference:
        while anum >= achars:
            rnum += rchars
            anum -= achars
    return rnum

def r_to_a(rnum: str) -> int:
    '''Convert roman numeral to arabic.'''
    if not isinstance(rnum, str):
        raise TypeError("r_to_a() only takes a roman numeral string")
    anum = 0
    for (rchars, achars) in RomanNum._RomanNum__reference:
        while rnum.startswith(rchars):
            anum += achars
            rnum = rnum[len(rchars):len(rnum)]
    if rnum:
        raise ValueError("invalid roman numeral string literal for RomanNum()")
    return anum

class RomanNum():
    '''Immutable roman numeral type, with common numeric operations supported.'''

    __reference = (
        ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400),
        ('C', 100), ('XC', 90), ('L', 50), ('XL', 40),
        ('X', 10), ('IX', 9), ('V', 5), ('IV', 4),
        ('I', 1)
    )

    def __init__(self, value):
        '''Initialize or updates a roman numeral type instance.'''
        if type(value) == RomanNum:
            self.__roman = value.roman()
            self.__arabic = value.arabic()
        elif type(value) == int:
            self.__roman = a_to_r(value)
            self.__arabic = value
        elif type(value) == str:
            self.__arabic = r_to_a(value)
            self.__roman = value
        else:
            raise TypeError("RomanNum() only takes a decimal integer or a roman numeral string")

    __call__ = __init__

    def __str__(self) -> str:
        '''Return the roman numeral as string.'''
        return self.__roman

    __repr__ = __str__

    def __int__(self) -> int:
        '''Return the corresponding arabic numeral as integer.'''
        return self.__arabic

    __index__ = __int__

    def __float__(self) -> float:
        '''Return the corresponding arabic numeral as floating-point.'''
        return float(self.__arabic)

    def __len__(self) -> int:
        '''Return the length of the roman numeral string.'''
        return len(self.__roman)

    roman = property(
        __str__,
        lambda self, value: self.__init__(str(value)),
        doc = '''The number in roman numeral form.'''
    )

    arabic = property(
        __int__,
        lambda self, value: self.__init__(int(value)),
        doc = '''The number in arabic numeral form.'''
    )

    def __eq__(self, other):
        if type(other) == RomanNum:
            other = other.__arabic
        return self.__arabic == other or self.__roman == other
    def __ne__(self, other):
        return not self.__eq__(other)

    def __lt__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return self.__arabic < other
    def __le__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return self.__arabic <= other
    def __gt__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return self.__arabic > other
    def __ge__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return self.__arabic >= other

    def __add__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic + other))
    def __sub__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic - other))
    def __mul__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic * other))
    def __pow__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic ** other))
    def __floordiv__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic // other))
    def __mod__(self, other):
        if type(other) != float and type(other) != int:
            other = RomanNum(other).__arabic
        return RomanNum(int(self.__arabic % other))

if __name__ == "__main__":
    match input("Select mode ('A' for to arabic, 'R' for to roman): "):
        case 'A':
            print("Number in arabic numerals:", r_to_a(input("Enter the number (roman string): ")))
        case 'R':
            print("Number in roman numerals:", a_to_r(int(input("Enter the number (arabic integer): "))))
        case _:
            print("Invalid mode.")
